#include "net_protocol_v1.h"
#include <loradevice.h>
#include <logger.h>
#include <sstream>

namespace cores
{
    namespace protocols
    {
        namespace net_v1
        {
            using namespace cores::devices;

            NetProtocol::NetProtocol()
            {
            }

            NetProtocol::~NetProtocol()
            {
            }

            int NetProtocol::length() const
            {
                return 4 + this->len();
            }

            int NetProtocol::toBuffer(uint8_t *buffer, const int &size)
            {
                uint16_t s = this->length();
                if (s > size)
                    return 0;

                auto pos = 0;
                buffer[pos++] = _header;
                buffer[pos++] = (s & 0xff);
                buffer[pos++] = (s >> 8);
                auto l = writeToBuffer(buffer + pos, size - pos);
                auto crc = crc16(buffer + pos, l);
                pos += l;
                buffer[pos++] = (crc >> 8);
                buffer[pos++] = (crc & 0xff);
                buffer[pos++] = _tailer;

                if (pos != s)
                    return 0;

                return pos;
            }

            void NetProtocol::setHeader(const uint8_t &header)
            {
                this->_header = header;
            }

            void NetProtocol::setTailer(const uint8_t &tailer)
            {
                this->_tailer = tailer;
            }

            uint8_t NetProtocol::header() const
            {
                return this->_header;
            }

            uint8_t NetProtocol::tailer() const
            {
                return this->_tailer;
            }

            uint16_t NetProtocol::crc16(const uint8_t *buffer, const int &size)
            {
                const uint16_t polynomial = 0xa001; //  使用多项式
                uint16_t crc = 0x0000;              //  初始CRC值
                uint16_t flag;

                for (auto current_byte = 0; current_byte < size; current_byte++)
                {
                    crc ^= (buffer[current_byte] << 8);

                    for (uint8_t i = 0; i < 8; i++)
                    {
                        flag = crc & 0x8000;
                        crc <<= 1;
                        if (flag)
                            crc ^= polynomial;
                    }
                }

                return crc;
            }

            NoDataUpload::NoDataUpload(const std::string &stationCode, const uint64_t &timestamp)
                : NetProtocol(), _stationCode(stationCode), _timestamp(timestamp)
            {
            }

            NoDataUpload::~NoDataUpload()
            {
            }

            int NoDataUpload::writeToBuffer(uint8_t *buffer, const int &size)
            {
                auto pos = 0;
                buffer[pos++] = 0x00;                  // 无数据上报
                buffer[pos++] = _stationCode.length(); // 站点编号长度
                const uint8_t *p = (const uint8_t *)(_stationCode.c_str());
                for (auto i = 0; i < _stationCode.length(); i++)
                    buffer[pos++] = p[i];
                *(uint64_t *)(buffer + pos) = _timestamp;
                pos += sizeof(uint64_t);
                return pos;
            }

            int NoDataUpload::len() const
            {
                return 1 + 1 + _stationCode.length() + sizeof(uint64_t) + 2;
            }

            TransDataUpload::TransDataUpload(const std::string &stationCode,
                                             const std::string &deptCode,
                                             OilTransaction *transData,
                                             const bool &is_test)
                : NetProtocol(), _stationCode(stationCode), _deptCode(deptCode), _transData(transData), _is_test(is_test)
            {
            }

            TransDataUpload::~TransDataUpload()
            {
            }

            int TransDataUpload::len() const
            {
                // 指令 1
                // 站点编号长度 1
                // 站点编号 编号长度 8
                // 区域编号 6
                // loraId 2
                // 枪号 1
                // 抬枪时间 4
                // 挂枪时间 4
                // 油品 1
                // 理论加油量 4
                // 实际加油量 4
                // 单价 4
                // 实际交易金额 4
                // 编码器值 4
                // 数据类型 1
                // 显示屏类型 1
                // 显示屏原始数据 16
                // 校验码 2
                return 1 + 1 + _stationCode.length() + 6 + 2 + 1 + 4 + 4 + 1 + 4 + 4 + 4 + 4 + 4 + 1 + 16 + 2;
            }

            int TransDataUpload::writeToBuffer(uint8_t *buffer, const int &size)
            {
                if (_transData == nullptr)
                    return 0;

                auto pos = 0;
                buffer[pos++] = (this->_is_test ? 0x02 : 0x01);
                buffer[pos++] = _stationCode.length();

                // 写入站点编码
                const uint8_t *p = (const uint8_t *)_stationCode.c_str();
                for (auto i = 0; i < _stationCode.length(); i++)
                    buffer[pos++] = p[i];

                // 写入区域编码
                p = (const uint8_t *)(_deptCode.c_str());
                for (auto i = 0; i < 6; i++)
                    buffer[pos++] = p[i];

                // 写入lora id
                *(uint16_t *)(buffer + pos) = _transData->lora_id;
                pos += 2;

                // 写入枪号
                buffer[pos++] = _transData->nozzle_code;

                // 写入抬枪时间
                *(uint32_t *)(buffer + pos) = _transData->beg_time;
                pos += 4;

                // 写入挂枪时间
                *(uint32_t *)(buffer + pos) = _transData->end_time;
                pos += 4;

                // 写入油品
                buffer[pos++] = _transData->category;

                // 写入理论加油量
                {
                    auto req = LoraDeviceManager::Get(_transData->lora_id, _transData->nozzle_code);
                    if (req == nullptr)
                        XERROR("has no param of lora id : %d, nozzle : %d", _transData->lora_id, _transData->nozzle_code);
                    assert(req);

                    uint32_t temp = _transData->encoder_val * req->rate * 0.01f + 0.5f;
                    *(uint32_t *)(buffer + pos) = temp;
                    pos += 4;
                }

                // 实际加油量
                *(uint32_t *)(buffer + pos) = _transData->volume;
                pos += 4;

                // 单价
                *(uint32_t *)(buffer + pos) = _transData->price;
                pos += 4;

                // 实际交易金额
                *(uint32_t *)(buffer + pos) = _transData->amount;
                pos += 4;

                // 编码器值
                *(uint32_t *)(buffer + pos) = _transData->encoder_val;
                pos += 4;

                // 数据类型
                buffer[pos++] = _transData->screen_raw_type;

                // 显示屏类型
                buffer[pos++] = _transData->screen_type;

                // 显示屏原始数据
                std::stringstream ss(_transData->screen_raw_data);
                ss >> std::hex;
                int value;
                for (auto i = 0; i < 16; i++)
                {
                    ss >> value;
                    buffer[pos++] = value;
                }

                return pos;
            }

            LlqDataUpload::LlqDataUpload(
                const std::string &stationCode,
                const std::string &deptCode,
                LlqDataItem *data,
                const bool &is_test)
                : NetProtocol(), _stationCode(stationCode), _deptCode(deptCode), _data(data), _is_test(is_test)
            {
            }

            LlqDataUpload::~LlqDataUpload()
            {
            }

            int LlqDataUpload::len() const
            {
                // 指令  1
                //  站点编号长度 1
                //  站点编号     站点编号长度    8
                //  区域编号     6
                //  油罐编号     1
                //  油品         1
                //  采集数据时间     4
                //  油高             4
                //  水高         4
                //  温高         4
                //  总体积       4
                //  油体积       4
                //  水体积       4
                //  校验码       2
                return 1 + 1 + _stationCode.length() + 6 + 1 + 1 + 4 + 4 + 4 + 4 + 4 + 4 + 4 + 2;
            }

            int LlqDataUpload::writeToBuffer(uint8_t *buffer, const int &size)
            {
                auto pos = 0;

                // 指令
                buffer[pos++] = _is_test ? 0x04 : 0x03;

                // 站点编号长度
                buffer[pos++] = _stationCode.length();

                // 站点编号
                uint8_t *p = (uint8_t *)_stationCode.c_str();
                for (auto i = 0; i < _stationCode.length(); i++)
                    buffer[pos++] = p[i];

                // 区域编号
                p = (uint8_t *)_deptCode.c_str();
                for (auto i = 0; i < 6; i++)
                    buffer[pos++] = p[i];

                // 油罐编号
                buffer[pos++] = _data->code;

                // 油罐油品
                buffer[pos++] = _data->category;

                // 数据时间
                *(uint32_t *)(buffer + pos) = _data->create_time;
                pos += sizeof(uint32_t);

                // 油高
                *(float *)(buffer + pos) = _data->oil_level;
                pos += sizeof(float);

                // 水高
                *(float *)(buffer + pos) = _data->water_level;
                pos += sizeof(float);

                // 温高
                *(float *)(buffer + pos) = _data->temperature;
                pos += sizeof(float);

                // 总体积
                *(float *)(buffer + pos) = _data->volume;
                pos += sizeof(float);

                // 油体积
                *(float *)(buffer + pos) = _data->oil_volume;
                pos += sizeof(float);

                // 水体积
                *(float *)(buffer + pos) = _data->water_volume;
                pos += sizeof(float);

                return pos;
            }

            AskForUpdateServiceReq::AskForUpdateServiceReq(
                const std::string &stationCode,
                const uint64_t &serviceTime,
                const uint8_t &errorCode)
                : NetProtocol(), _stationCode(stationCode), _serviceTime(serviceTime), _errorCode(errorCode)
            {
            }

            AskForUpdateServiceReq::~AskForUpdateServiceReq()
            {
            }

            int AskForUpdateServiceReq::len() const
            {
                // 指令     0xff    1
                // 站点编号长度 1
                // 站点编号     站点编号长度    8
                // 服务器时间   8
                // 错误码       1
                // 校验码   2
                return 1 + 1 + _stationCode.length() + 8 + 1 + 2;
            }

            int AskForUpdateServiceReq::writeToBuffer(uint8_t *buffer, const int &size)
            {
                auto pos = 0;
                // 写入指令
                buffer[pos++] = 0xff;

                // 写入站点编号长度
                buffer[pos++] = _stationCode.length();

                // 写入站点编号
                const uint8_t *p = (const uint8_t *)_stationCode.c_str();
                auto l = _stationCode.length();
                for (auto i = 0; i < l; i++)
                    buffer[pos++] = p[i];

                // 写入服务器时间
                *(uint64_t *)(buffer + pos) = _serviceTime;
                pos += sizeof(uint64_t);

                // 写入错误码
                buffer[pos++] = _errorCode;
                return pos;
            }

            AskForUpdate::AskForUpdate(const std::string &stationCode, const uint64_t &now_time)
                : NetProtocol(), _stationCode(stationCode), _currentTime(now_time)
            {
            }

            AskForUpdate::~AskForUpdate()
            {
            }

            int AskForUpdate::len() const
            {
                // 指令     0xa0    1
                // 站点编号长度 1
                // 站点编号     站点编号长度    8
                // 服务器时间   8
                // 校验码   2
                return 1 + 1 + _stationCode.length() + 8 + 2;
            }

            int AskForUpdate::writeToBuffer(uint8_t *buffer, const int &size)
            {
                auto pos = 0;
                // 写入指令
                buffer[pos++] = 0xff;

                // 写入站点编号长度
                buffer[pos++] = _stationCode.length();

                // 写入站点编号
                const uint8_t *p = (const uint8_t *)_stationCode.c_str();
                auto l = _stationCode.length();
                for (auto i = 0; i < l; i++)
                    buffer[pos++] = p[i];

                // 写入服务器时间
                *(uint64_t *)(buffer + pos) = _currentTime;
                pos += sizeof(uint64_t);

                return pos;
            }

            UpdateServiceAccept::UpdateServiceAccept(
                const std::string &stationCode,
                const uint64_t &serviceTime,
                const uint8_t &errorCode)
                : AskForUpdateServiceReq(stationCode, serviceTime, errorCode)
            {
            }

            UpdateServiceAccept::~UpdateServiceAccept()
            {
            }

            int UpdateServiceAccept::writeToBuffer(uint8_t *buffer, const int &size)
            {
                auto result = AskForUpdateServiceReq::writeToBuffer(buffer, size);
                buffer[0] = 0xf0;
                return result;
            }

            NotifyServiceUpdated::NotifyServiceUpdated(
                const std::string &stationCode,
                const uint64_t &now_time)
                : AskForUpdate(stationCode, now_time)
            {
            }

            NotifyServiceUpdated::~NotifyServiceUpdated()
            {
            }

            int NotifyServiceUpdated::writeToBuffer(uint8_t *buffer, const int &size)
            {
                auto result = AskForUpdate::writeToBuffer(buffer, size);
                if (result > 0)
                    buffer[0] = 0xa1; // 告知已更新
                return result;
            }

            UpdatedResponse::UpdatedResponse(const std::string &stationCode,
                                             const uint64_t &serviceTime,
                                             const uint8_t &errorCode)
                : AskForUpdateServiceReq(stationCode, serviceTime, errorCode)
            {
            }

            UpdatedResponse::~UpdatedResponse()
            {
            }

            int UpdatedResponse::writeToBuffer(uint8_t *buffer, const int &size)
            {
                auto result = AskForUpdateServiceReq::writeToBuffer(buffer, size);
                if (result > 0)
                    buffer[0] = 0xf1;
                return result;
            }

            UploadIPReq::UploadIPReq(const string &stationCode, const uint64_t &gateTime)
                : AskForUpdate(stationCode, gateTime)
            {
            }

            UploadIPReq::~UploadIPReq()
            {
            }

            int UploadIPReq::writeToBuffer(uint8_t *buffer, const int &size)
            {
                auto result = AskForUpdate::writeToBuffer(buffer, size);
                if (result > 0)
                    buffer[0] = 0xa2;
                return result;
            }

            IPResponseReq::IPResponseReq(const std::string &stationCode,
                                         const string &ip,
                                         const uint16_t &port,
                                         const uint64_t &serviceTime,
                                         const uint8_t &errorCode)
                : NetProtocol(), _stationCode(stationCode), _ip(ip), _port(port), _serviceTime(serviceTime), _errorCode(errorCode)
            {
            }

            IPResponseReq::~IPResponseReq()
            {
            }

            int IPResponseReq::writeToBuffer(uint8_t *buffer, const int &size)
            {
                auto pos = 0;
                buffer[pos++] = 0xf2;
                buffer[pos++] = _stationCode.length();
                uint8_t *p = (uint8_t *)_stationCode.c_str();
                auto l = _stationCode.length();
                for (auto i = 0; i < l; i++)
                    buffer[pos++] = p[i];

                // 将IP地址转换成四个数字
                {
                    string patter(".");
                    string splitter(" ");
                    string temp = _ip;
                    auto pos = temp.find(patter);
                    while (pos != string::npos)
                    {
                        temp.replace(pos, patter.length(), splitter);
                        pos += splitter.length();
                        pos = temp.find(patter, pos);
                    }

                    stringstream ss(temp);
                    int data;
                    for (auto i = 0; i < 4; i++)
                    {
                        ss >> data;
                        buffer[pos++] = data;
                    }
                }

                // 写端口号
                *(uint16_t*)(buffer + pos) = _port;     pos += sizeof(uint16_t);

                // 写服务器时间
                *(uint64_t*)(buffer + pos) = _serviceTime;      pos += sizeof(uint64_t);

                buffer[pos++] = _errorCode;

                return pos;
            }

            int IPResponseReq::len() const
            {
                // 指令     0xa0    1
                // 站点编号长度 1
                // 站点编号     站点编号长度    8
                // IP地址       4
                // 端口号       2
                // 服务器时间   8
                // 错误码
                // 校验码   2
                return 1 + 1 + _stationCode.length() + 4 + 2 + 8 + 1 + 2;
            }

            CloundDebugSend::CloundDebugSend(const string &stationCode, const uint64_t &gatetime)
                : AskForUpdate(stationCode, gatetime)
            {
            }

            CloundDebugSend::~CloundDebugSend()
            {
            }

            int CloundDebugSend::writeToBuffer(uint8_t *buffer, const int &size)
            {
                auto result = AskForUpdate::writeToBuffer(buffer, size);
                if (result > 0)
                    buffer[0] = 0x99;
                return result;
            }

            CloundDebugAck::CloundDebugAck(const std::string &stationCode,
                                           const uint64_t &serviceTime,
                                           const uint8_t &errorCode)
                : AskForUpdateServiceReq(stationCode, serviceTime, errorCode)
            {
            }

            CloundDebugAck::~CloundDebugAck()
            {
            }

            int CloundDebugAck::writeToBuffer(uint8_t *buffer, const int &size)
            {
                auto result = AskForUpdateServiceReq::writeToBuffer(buffer, size);
                if (result > 0)
                    buffer[0] = 0xa9;
                return result;
            }
        }
    }
}